﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace StateManager
{
    enum plyrDirection
    {
        up = 0,
        down,
        left,
        right
    }
    /// <summary>
    /// The player
    /// </summary>
    class Player
    {
        // the player class stores a level object
        private Level plyrLevel;

        // this keyboardstate object is used to store the previous state that was given
        private KeyboardState keysprev;
        private GamePadState GPPrev;
        
        // this object stores a font which allows for writing text on screen
        private SpriteFont sF;

        int playerNumber;

        // a boolean and a properties which is set whether the player is in water or not
        public Boolean onWater
        {
            get { return OnWater; }

        }
        Boolean OnWater = false;

        private int countThing = 0;

        public int CountThing
        {
            set { countThing = value; }
        }

        bool hasWon = false;

        public int PlayerNum
        {
            get { return playerNumber; }
        }

        public bool HasWon
        {
            get { return hasWon; }
        }

        bool isMoving = false;

        public bool IsMoving
        {
            get { return isMoving; }
            set { isMoving = value; }
        }

        bool Dead;
        public bool isDead
        {
            get { return Dead; }
        }

        // these constants hold how far the player can move in pixels -- 40 horizontally and 32 vertically
        // these values will be changed if there are any changes to the size of the tiles
        const float movemaxX = 40;
        const float movemaxY = 32;

        plyrDirection storedDir;

        SoundEffect boing;

        float timeWaitAdd;
        const float deathWaitTime = 2000;

        Color frogColor = Color.White;

        public Color FrogColor
        {
            get { return frogColor; }
        }
             
        
        //private Animation plyrRun;
        private Animation plyrJump;
        private Animation plyrJumpH;
        private Animation plyrIdle;
        private Animation plyrIdleH;
        private Animation plyrDrown;
        private Animation plyrDrownH;
        private Animation plyrDeadH;
        private Animation plyrDead;
        //private Animation plyrBlood;
        //private Animation plyrBloodH;

        //private Animation plyrJumpHorizontal;
        //private Animation plyrJumpVertical;
        private PlayerAnimation sprite;
        //private Animation plyrRoadKill;
        

        // this rectangle is used to check whether if the player is within the screen
        Rectangle viewPort;

        // this is only a template of the player for development, when the base is done
        // then the player animations will be used instead
        private Texture2D texture;
        
        // A Vector2 object stores two floating point numbers.  This will be used to store the location
        // of the player in the game
        private Vector2 pos;

        // this property returns a bounding rectangle which is given the players x and y co-ordinates
        // as well as the width and height of the image thats loaded
        public Rectangle plyrRect
        {
            get
            {
                return new Rectangle((int)pos.X + 16, (int)pos.Y, 48, 48);
            }
        }
        //private const float MoveStickScale = 1.0f;
        // this bool states whether if the player is dead or alive
        public bool IsAlive
        {
            get { return isAlive; }
        }
        bool isAlive = false;

        int lives;

        int score;

        public int Score
        {
            get { return score; }
            set { score = value; }
        }

        public int Lives
        {
            get { return lives; }
            set { lives = value; }
        }

        // This property returns the players position
        public Vector2 Position
        {
            get { return pos; }
        }
        public Player(Level lvl, int playerNum, bool aliveSet)
            : this(lvl)
        {
            // this constructor is for the multi plyaer game, the alive or dead state is set here so that players that died cannot enter the next round
            Dead = !aliveSet;

            if (!Dead)
                isAlive = true; 
            playerNumber = playerNum;
            lives = 3;
            loadContent(lvl);
            sprite.PlayAnimation(plyrIdle);

            pos = new Vector2((viewPort.Width / 3) - texture.Width * (playerNumber + 1), viewPort.Height - texture.Height);
        }

        public Player(int score, int lives, Level lvl)
            : this(lvl)
        {
            // this constructor is for the single player game, the amount of lives are stored when the level and the player is created
            this.score = score;
            this.lives = lives;
            loadContent(lvl);
            sprite.PlayAnimation(plyrIdle);
            pos = new Vector2((viewPort.Width / 2) - (texture.Width/2), viewPort.Height - texture.Height);
            //lives = 3;
            //lives = 
        }

        // the player constructor which takes a level object as an arguement
        public Player(Level level)
        {
            sprite = new PlayerAnimation();
            //
            // the player.level object is assigned the arguement object
            plyrLevel = level;
            //lives = 1;
            viewPort = level.viewPort;
            
            isAlive = true;
            hasWon = false;

            
            // temporary!!
        }
        
        // this method loads the player image into memory
        public void loadContent(Level lvl)
        {
            
            texture = lvl.Content.Load<Texture2D>("images/frog");

            sF = lvl.Content.Load<SpriteFont>("font/hudFont");

            boing = lvl.Content.Load<SoundEffect>("sounds/players/boing");

            plyrJump = new Animation(lvl.Content.Load<Texture2D>("images/frunsmllv"), 0.05f,true,48,16);
            plyrIdle = new Animation(lvl.Content.Load<Texture2D>("images/frog"), 0.1f, true,80,0);
            plyrIdleH = new Animation(lvl.Content.Load<Texture2D>("images/frogh"), 0.1f, false, 80,0);
            plyrJumpH = new Animation(lvl.Content.Load<Texture2D>("images/frunsmll"), 0.05f, true, 80,0);
            plyrDrown = new Animation(lvl.Content.Load<Texture2D>("images/frogdn"), 0.1f, false, 80,0);
            plyrDrownH = new Animation(lvl.Content.Load<Texture2D>("images/fdrownh"), 0.1f, false, 48, 16);
            plyrDeadH = new Animation(lvl.Content.Load<Texture2D>("images/thing2fdh"), 0.1f, false, 48, 16, -16);
            plyrDead = new Animation(lvl.Content.Load<Texture2D>("images/thing2fd"), 0.1f, false, 80, 0);


            switch (playerNumber)
                {
                case 0:
                    frogColor = Color.Red;
                    break;
                case 1:
                    frogColor = Color.Blue;
                    break;
                case 2:
                    frogColor = Color.Orange;
                    break;
                case 3:
                    frogColor = Color.Purple;
                    break;
                }



        }

        public void playPlayerSound()
        {
            boing.Play();
        }

        public void respawnPlayer()
        {
            pos = new Vector2(viewPort.Width / 2 - texture.Width / 2, viewPort.Height - texture.Height);
            // the position is set to the middle of the screen and the bottom of the game
            isMoving = false;
            isAlive = true;
            countThing = 0;
            sprite.PlayAnimation(plyrIdle);
            GamePad.SetVibration(PlayerIndex.One, 0, 0);
            // i have to make sure that this code is run or the controller never stops shaking
        }

        // this method kills the player
        public void killPlayer(char thing)
        {
            if (isAlive) // if the player is alive
            {
                isAlive = false;
                switch (thing)
                {
                    // d = drown, c = killed
                        // there are two different sprite sheets for each animation, depending on which way the player is facing
                    case 'd': // if the player is facing left or right
                        if (storedDir == plyrDirection.left || storedDir == plyrDirection.right)
                            sprite.PlayAnimation(plyrDrownH); // play the horizontal animation sprite sheet
                        else 
                            sprite.PlayAnimation(plyrDrown);// just play the standard animatino sprite sheet
                        break;
                    case 'c':
                        if (storedDir == plyrDirection.left || storedDir == plyrDirection.right)
                            sprite.PlayAnimation(plyrDeadH); // the same is here
                        else
                            sprite.PlayAnimation(plyrDead);
                        break;
                }
                
                
                lives--;



                if (lives == 0)
                    Dead = true;
                // for now the players location is reset to the right corner of the screen but
                // this will be replaced
                GamePad.SetVibration(PlayerIndex.One, 0, 0);
                // the rumble motor has to manually be shut off if it is rumbling
            }
        }


        // this method moves the player in the X axis with the enum dir which is casted as a float.
        public void movePlayer(float vel, Direction dir)
        {
            switch (dir)
            {
                case Direction.Left: // if the log that the player is on is going left
                    pos.X -= vel; // make the player go left
                    break;
                case Direction.Right: // else if its right
                    pos.X += vel;  // make the player go right
                    break;
            }
        }
        // this method performs all of the game logic for the player, such as movement
        public void Update(KeyboardState kps, GamePadState gps, GameTime gameTime)
        {
            //plyrDirection dir = plyrDirection.up;
            if (isAlive && !hasWon && !Dead)
            {
                // if one of the arrow keys is pressed and was not pressed before hand,
                // then the player moves

                if (!isMoving)
                {
#if !XBOX
                    if (playerNumber == 0)
                    // since I can't change the player number for the controller on the pc, the keyboard is player
                    // two.  This means that a controller is REQUIRED to play single player.
                    {
                        // check to see if the player has hit the key and didn't hit the key previously so that the player can't zoom across the level
                        // while holding down the key
                        if (kps.IsKeyDown(Keys.Left) && keysprev.IsKeyUp(Keys.Left))
                        {
                            isMoving = true;
                            storedDir = plyrDirection.left;
                            GamePad.SetVibration((PlayerIndex)playerNumber, 0.5f, 0.5f);
                            playPlayerSound();
                            sprite.PlayAnimation(plyrJumpH);
                        }
                        if (kps.IsKeyDown(Keys.Right) && keysprev.IsKeyUp(Keys.Right))
                        {              
                            isMoving = true;
                            storedDir = plyrDirection.right;
                            playPlayerSound();
                            sprite.PlayAnimation(plyrJumpH);
                        }
                        if (kps.IsKeyDown(Keys.Up) && keysprev.IsKeyUp(Keys.Up))
                        {                 
                            isMoving = true;
                            storedDir = plyrDirection.up;
                            score += 100;
                            playPlayerSound();
                            sprite.PlayAnimation(plyrJump);
                        }
                        if (kps.IsKeyDown(Keys.Down) && keysprev.IsKeyUp(Keys.Down))
                        {
                            isMoving = true;
                            storedDir = plyrDirection.down;
                            playPlayerSound();
                            sprite.PlayAnimation(plyrJump);
                        }
                        
                    } // we're not gonna let the keyboard take control of everything
#endif   
                    if (gps.IsButtonDown(Buttons.DPadLeft) && GPPrev.IsButtonUp(Buttons.DPadLeft))
                    {
                        isMoving = true;
                        storedDir = plyrDirection.left;
                        playPlayerSound();
                        sprite.PlayAnimation(plyrJumpH);
                        GamePad.SetVibration((PlayerIndex)playerNumber, 0.5f, 0.5f);
                    }
                    if (gps.IsButtonDown(Buttons.DPadRight) && GPPrev.IsButtonUp(Buttons.DPadRight))
                    {
                        isMoving = true;
                        storedDir = plyrDirection.right;
                        playPlayerSound();
                        sprite.PlayAnimation(plyrJumpH);
                        GamePad.SetVibration((PlayerIndex)playerNumber, 0.5f, 0.5f);
                    }
                    if (gps.IsButtonDown(Buttons.DPadUp) && GPPrev.IsButtonUp(Buttons.DPadUp))
                    {
                        isMoving = true;
                        storedDir = plyrDirection.up;
                        playPlayerSound();
                        score += 100;
                        sprite.PlayAnimation(plyrJump);
                        GamePad.SetVibration((PlayerIndex)playerNumber, 0.5f, 0.5f);
                    }
                    if (gps.IsButtonDown(Buttons.DPadDown) && GPPrev.IsButtonUp(Buttons.DPadDown)) 
                    {
                        isMoving = true;
                        storedDir = plyrDirection.down;
                        playPlayerSound();
                        sprite.PlayAnimation(plyrJump);
                        GamePad.SetVibration((PlayerIndex)playerNumber, 0.5f, 0.5f);
                    }

                }
                else
                {
                    updateMovement();
                    
                }

                // this is set so that the player can't hold down the key to zoom accross the level

                keysprev = kps; // the keyboardstate keysprev is assigned the current keyboardstate
                GPPrev = gps;
                // this is to check if any keys were released or changed
               

            }
            else if (!isAlive && lives > 0)
            {
                timeWaitAdd += gameTime.ElapsedGameTime.Milliseconds;

                if (timeWaitAdd >= deathWaitTime)
                {
                    respawnPlayer();
                    timeWaitAdd -= deathWaitTime;
                }
            }
            
        }

        public void playerWin()
        {
            if (!hasWon)
                hasWon = true;
            //pos = Vector2.Zero;
            GamePad.SetVibration((PlayerIndex)playerNumber, 0, 0);
        }

        // this method updates the movement so that the players move to a preset location at a smooth rate
        public void updateMovement()
        {
            if (isMoving)
            {
                switch (storedDir)
                {
                    case plyrDirection.up:
                        pos.Y -= texture.Height / 4;
                        countThing += texture.Height / 4;
                        break;
                    case plyrDirection.down:
                        pos.Y += texture.Height / 4;
                        countThing += texture.Height / 4;
                        break;
                    case plyrDirection.left:
                        pos.X -= texture.Width / 4;
                        countThing += texture.Width / 4;
                        break;
                    case plyrDirection.right:
                        pos.X += texture.Width / 4;
                        countThing += texture.Width / 4;
                        break;
                    default:
                        break;
                }
                if (storedDir == plyrDirection.up || storedDir == plyrDirection.down)
                {
                    if (countThing >= texture.Height)
                    {
                        countThing = 0;
                        isMoving = false;
                        sprite.PlayAnimation(plyrIdle);
                        GamePad.SetVibration((PlayerIndex)playerNumber, 0, 0);
                    }
                }
                else
                {
                    if (countThing >= texture.Width)
                    {
                        countThing = 0;
                        isMoving = false;
                        sprite.PlayAnimation(plyrIdleH);
                        GamePad.SetVibration((PlayerIndex)playerNumber, 0, 0);
                    }
                }
            }
        }

        public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
        {
            //Color frogcolor = Color.White;
            if (!Dead && !hasWon)
            {
                //spriteBatch.Draw(texture, pos, Color.White);
                switch (storedDir)
                {
                    case plyrDirection.up:
                        sprite.Draw(gameTime, spriteBatch, pos, SpriteEffects.None, frogColor);
                        break;
                    case plyrDirection.down:
                        sprite.Draw(gameTime, spriteBatch, pos, SpriteEffects.FlipVertically, frogColor);
                        break;
                    case plyrDirection.left:
                        sprite.Draw(gameTime, spriteBatch, pos, SpriteEffects.None, frogColor);
                        break;
                    case plyrDirection.right:
                        sprite.Draw(gameTime, spriteBatch, pos, SpriteEffects.FlipHorizontally, frogColor);
                        break;
                    default:
                        throw new NotSupportedException("How did you send a player direction enum that doesn't match up?!");
                }
            }
            
        }
    }
}
